home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / oper_sys / presto / prest_04.lha / src / threads.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-08-08  |  16.8 KB  |  743 lines

  1. /*
  2.  * threads.c
  3.  *
  4.  *    Main implementation for threads.
  5.  *                2/4/88
  6.  *                bnb
  7.  *                streamline free pool code
  8.  *
  9.  *    Last modified:        1/6/88
  10.  *    by:            bnb
  11.  *    reason:            remove knowledge of specific sycnchroobjects
  12.  *
  13.  *    Last modified:        1/2/88
  14.  *    by:            bnb
  15.  *    reason:            willjoin and join
  16.  *
  17.  *
  18.  *    Last modified:        12/21/87
  19.  *    by:            bnb
  20.  *    reason:            nuke old version (non asm) of swtch
  21.  *
  22.  *    Last modified:        8/15/88
  23.  *    by:            chase
  24.  *    reason:            VAX/Unix and other support etc.
  25.  */
  26.  
  27. #define _THREADS_C
  28.  
  29. #include "presto.h"
  30. #include "machdep.h"
  31. #include "atomic_int.h"        /* for counter */
  32.  
  33. //
  34. // thisthread ALWAYS refers to the currently running thread in the
  35. // context of the user.  thisthread must be private to each processor,
  36. // but is set on context switches appropriately.
  37. // Use staticthread to give us something to reference into before
  38. // the system gets going during the static construction.
  39. //
  40.  
  41. private_t Thread staticthread(-1);
  42. Thread *systhread = &staticthread;
  43.  
  44. private_t Thread *thisthread = &staticthread;
  45. private_t int _rtmp;    // must be private (hack to simplify asm movs)
  46.  
  47. //
  48. // maintain a free list of thread templates.  
  49. //
  50.  
  51. #define THREADPOOL_HIWATER        100    /*save only this much*/
  52. static shared_t ThreadQ t_free(TS_ANY);
  53. static shared_t AtomicInt t_freetag;
  54.  
  55. //
  56. //
  57. // Some notes on constructors:
  58. //    Automatic constructors of base and sub classes are painful.
  59. //    If we are resurrecting an old thread, we don't need to go
  60. //    through all of the  rigamarole to reinit its member
  61. //    elements.  Instead, when first constructed,
  62. //    we call "init" on them, and 
  63. //    let them do the work involved in making themnselves for the
  64. //    first time.  After that, they are presumed to be "reusable"
  65. //    after calling "reinit".
  66. //
  67.  
  68. Thread::Thread(char *name, int tid, long ssiz, int musthavestack)
  69.                             : (OBJ_THREAD, name)
  70. {
  71.     Thread *t;
  72.  
  73.     if (tid < 0)
  74.         error("Invalid thread id");
  75.     if (ssiz < 0)
  76.         error("Negative stack size");
  77.     
  78.     if (this == 0)    {        // no derived class
  79.         t = t_free.get();
  80.         if (t == 0)        // can't find anything
  81.             t = (Thread*)new char[sizeof(Thread)];
  82.         t->t_slockcount = 0;
  83.         t->t_callstate.init();    // intentional ctor
  84.         t->t_expired = 0;
  85.         this = t;
  86.     } else    {            // derived class
  87.         this = this;
  88.         this->t_slockcount = 0;
  89.         this->t_callstate.init();
  90.         this->t_expired = 0;
  91.     }    
  92.  
  93. #ifdef debug
  94.     dout << form("thisthread creating thread %s (%x)\n",
  95.         name, this);
  96. #endif debug
  97.  
  98.     t_flags = 0;
  99.     t_data = (Objany)0;
  100.     
  101.     if (ssiz == 0)    {    // run on existing stack
  102.         t_stack = new Stack(0);
  103.         t_flags = TF_KEEPSTACK;
  104.     } else    if (musthavestack)    {
  105.         t_stack = new Stack(ssiz);
  106.     } else    {
  107.         t_neededstacksize = ssiz;        // union
  108.         t_flags |= TF_INCOMPLETE;
  109.     }
  110.     
  111.     t_csp = 0;
  112.     t_fp = 0;
  113.     // t_stack already set
  114.     t_state =  TS_IDLE;
  115.     // t_flags already set
  116.     t_tag = t_freetag++;    
  117.     t_tid = (tid > 0)? tid : t_tag;
  118.     t_pri = TP_BASEPRIO;
  119.     t_proc = 0;            // running on
  120.     t_blockedon = 0;        // waiting on
  121.     t_jthread = 0;            // joining thread
  122. }
  123.  
  124. //
  125. // Thread constructor for staticthread.
  126. //
  127.  
  128. extern Process staticproc;
  129.  
  130. Thread::Thread(int tid)
  131. {
  132.     setflags(TF_SCHEDULER | TF_NONPREEMPTABLE);
  133.     setproc(&staticproc);
  134.     t_expired = 0;
  135.     t_slockcount = 0;
  136.     t_tid = tid;
  137. }
  138.  
  139. //
  140. // "Virtual" constructor
  141. //
  142. Thread*
  143. Thread::newthread(char *name, int tid, long ssiz, int musthavestack)
  144. {
  145.     return new Thread(name,tid,ssiz,musthavestack);
  146. }
  147.  
  148.  
  149. //
  150. // Delete a thread:
  151. //        When a thread comes here, it will never run again.
  152. //        Always return it's stack to the free pool.
  153. //        If the thread will be joined upon, don't restore it to
  154. //        the thread free pool, otherwise do.
  155. //        
  156. //        if we make it to the final return, the regular deallocation
  157. //        will take place.
  158. //
  159. //
  160. //        We make sure that the guy being deleted is
  161. //        actually finished.   Eventually, we should be
  162. //        able to halt him midstream, for now, we just
  163. //        wait until he is done.  This is used by join
  164. //        to make sure that we don't destroy a thread
  165. //        before it has finished executing.
  166. //
  167. Thread::~Thread()
  168. {
  169.     if (this == &staticthread) {
  170.         this = 0;
  171.         return;
  172.     }
  173.  
  174.     while (t_state&TS_RUNNING)
  175.         ;
  176.  
  177.     if (((t_state&TS_FINISHED) == 0) && ((t_state&TS_DELETE) == 0))
  178.         error("Destroying an unfinished thread");
  179.  
  180.     delete t_stack;            // always reuse        
  181.     
  182.     if ((t_state&TS_DELETE) == 0 && t_free.length() < THREADPOOL_HIWATER) {
  183.         t_free.append(this);
  184.         this = 0;
  185.     } 
  186.  
  187.     // if this != 0, regular deallocation will occur here.
  188. }
  189.  
  190.  
  191. //
  192. // Start a thread running in some invocation function of an object.
  193. //    We save the callstate from the the start call and use it
  194. //    later when we actually get this puppy scheduled.  We are
  195. //    trying not to be machine dependent here, but we are still
  196. //    assuming:
  197. //    -    sizeof(Objany) == sizeof(PFany) == sizeof(int*)
  198. //    -    args are pushed in the stack in a sensible order so
  199. //        we can figure out what comes next
  200. //    --    that we can figure out how many longwords we
  201. //        were called with through some func nargs().
  202. //        On the vax this is just an inspection of *(ap).  On
  203. //        the ns32000 we can get away with using libpps's nargs()
  204. //        though what they are doing is very simple (see nargs.c 
  205. //        in the distribution directory).
  206. //
  207. //    Fork:
  208. //        if (this == thisthread) 
  209. //        then we just start up an asynchronous invocation using 
  210. //        an anonymous
  211. //        thread having the same params as the starting thread.
  212. //        This allows people to make asynchronous invocation
  213. //        without having to create threads explicitly.  First arg
  214. //        specifies if they will join on the thread or not.  Forked
  215. //        thread is returned.
  216. // 
  217. //
  218.  
  219. #define NUMSTARTARGS    3
  220.  
  221. int
  222. Thread::start(Objany obj, PFany pf, ...)
  223. {
  224.     extern int nargs();
  225.     
  226.     if ( (t_state&TS_IDLE) == 0)
  227.         error("Can't start an invocation in a busy thread");
  228.     
  229.     //
  230.     // We don't want the hidden first arg this, obj, or pf.
  231.     //    -NUMSTARTARGS is the first argument that we need to save.
  232.     //
  233. /*XX MACHDEP */
  234.     t_callstate.set(pf, nargs()-NUMSTARTARGS, ((int*)(&pf)+1));
  235.     t_start1(obj);
  236.     return 0;
  237. }
  238.         
  239.     
  240. #define NUMFORKARGS    4
  241.  
  242. Thread*
  243. Thread::fork(int needjoin, Objany obj, PFany pf, ...)
  244. {
  245.     register Thread* startthread;
  246.     extern int nargs();
  247.     
  248.     if (this == thisthread)    {
  249.         // newthread here
  250.         startthread = newthread(name(), 0, stack()->size());
  251.         if (startthread == 0)
  252.             thisthread->error("Can't create asynch invocation");
  253.     }  else        {
  254.         // can't control thread if its not thisthread
  255.         // must disallow forking on threads other than thisthread
  256.         this->error("Can only fork off of thisthread");
  257.     }
  258.             
  259.     if (needjoin == TF_WILLJOIN)
  260.         startthread->willjoin();
  261.     
  262.     //
  263.     // We don't want the hidden first arg this, needjoin, obj, or pf.
  264.     //    -NUMFORKARGS is the first argumennt that we need to save.
  265.     // 
  266. /*XX MACHDEP */
  267.     startthread->t_callstate.set(pf, nargs()-NUMFORKARGS, ((int*)(&pf)+1));
  268.     startthread->t_start1(obj);
  269.     return startthread;
  270. }
  271.  
  272.  
  273.  
  274. //
  275. // Pass new thread to the scheduler
  276. //
  277. void
  278. Thread::t_start1(Objany obj)
  279. {
  280.     extern shared_t ThreadQ    *preschedthreads;
  281.     t_boundobj = obj;
  282.     setstate(TS_VIRGIN);
  283.     if ((t_flags&TF_SCHEDULER)==0) {    
  284.         // don't schedule the scheduler 
  285.         if (sched)
  286.             sched->begin(this); 
  287.         else    {            // must deal with early thread
  288.             if (preschedthreads == 0)
  289.                 preschedthreads = new ThreadQ(TS_VIRGIN);
  290.             preschedthreads->append(this);
  291.         }
  292.     }
  293.     return;
  294. }
  295.  
  296.  
  297. //
  298. // Spinning wait for thread to stop running
  299. //
  300. void
  301. Thread::isrunning2()
  302. {
  303.     while (t_state&TS_RUNNING)
  304.         ;
  305. }
  306.  
  307.  
  308.  
  309. //
  310. // Why do we need to know why we woke up?  I don't think so
  311. //
  312.  
  313. void
  314. Thread::wakeup(SynchroObject* so = 0)
  315. {
  316.     //
  317.     // Hold off on waking a thread that is running, but going to sleep.
  318.     // Not doing so may cause us to trash the thread's object fields.
  319.     //
  320.     isrunning2();
  321.         
  322.     if ( (t_state & (TS_BLOCKED)) == 0)
  323.         error("Waking a non-blocked object");
  324.  
  325.     if (so && t_blockedon != so)    
  326.         error("Wokeup on the wrong thing!");
  327.         
  328.     andstate(~(TS_BLOCKED));    
  329.     t_blockedon = 0;
  330.     sched->resume(this);
  331. }
  332.  
  333.  
  334. int
  335. Thread::run()
  336. {
  337.     int    results;
  338.     Thread *schedthread = thisthread;
  339.  
  340.     this->setproc(thisproc);
  341.  
  342.     thisthread = this;
  343.  
  344.     schedthread->isnotrunning();
  345.     this->isrunning();
  346.     results = this->runrun();        // BOOM
  347.     this->isnotrunning();
  348.  
  349.     // if finished, and nobody will join on this thread, delete it.
  350.     if (t_state & TS_FINISHED)
  351.         if ((t_flags & TF_WILLJOIN) == 0)
  352.             delete this;
  353.  
  354.     thisthread = schedthread;
  355.  
  356.     schedthread->isrunning();
  357.     return results;
  358. }
  359.  
  360.  
  361. // 
  362. // runrun:
  363. //
  364. // This is a very central and somewhat difficult routine.
  365. //
  366. //
  367. // Begin the invocation in the invocation object using the current
  368. // thread running in the current processor
  369. //
  370. // This is essentialy a synchronous fork.  Control returns the processor
  371. // thread when forked thread blocks or terminates via a swtch back
  372. // from the invocation function.
  373. //
  374. // We don't worry about saving the regs on the right stack when we do
  375. // the stack switch inside the virgin test.  No register context ever
  376. // needs to be restored since threads, once they terminate, never return.
  377. //
  378.  
  379. int
  380. Thread::runrun()
  381. {
  382.     int dummy;            /// MUST BE FIRST
  383. #ifdef vax
  384.     int _rtmp = 0;            /// MUST BE SECOND
  385. #endif vax
  386.  
  387. #ifdef debug
  388. dout << form("runrun: begin thread %x\n", this);
  389. #endif debug
  390.  
  391. //    t_timer.timerstart();  XXX
  392.     t_expired = 0;
  393.  
  394.     if (t_state & TS_VIRGIN)        {
  395.         //
  396.         // Thread must be nonpreemptable while we are first
  397.         // initializing it (a swtch back before it has enough
  398.         // state to determine to whom we should switch would
  399.         // be bad news)
  400.  
  401.         //
  402.         // if we are not a whole thread, become one.  Force
  403.         // Stack to be something of size t_neededstacksize;
  404.         //
  405.         if (t_flags&TF_INCOMPLETE)    {    // try again
  406.             t_flags &= ~TF_INCOMPLETE;
  407.             t_stack = new Stack(t_neededstacksize);
  408.             if (t_stack == 0)
  409.                 error("Null stack in runrun");
  410.         }
  411.  
  412. #if    (i386 || sun)
  413.         //
  414.         // Insure stack of calling (process) thread is cleanly restored
  415.         // (including registers) when new thread next switches.
  416.         //
  417.         // Do this cleanly -- push a proper "swtch" context in
  418.         // current stack, and return on new stack.  When the
  419.         // thread swtch()'s back, will return from runrun(), but
  420.         // with proper register state.  Must insure runrun()
  421.         // doesn't have any register variables (if so, need to get
  422.         // them from the stack).  Older technique (ns32000,
  423.         // anyhow) has stack-pointer pass frame-pointer, then
  424.         // adjust frame pointer back on 1st swtch() back from new
  425.         // thread, and restores "random" register contents.
  426.         //
  427.         // First call to runrun() by "slave" process thread starts
  428.         // process thread itself; in this case, TF_KEEPSTACK is on.
  429.         // Since keeping calling stack, have the new stack top out
  430.         // at the dummy variable in this procedure (leaving enough
  431.         // room for swtch() context).
  432.         //
  433.         extern init_stack(Thread*,int*);
  434.         init_stack(this, (this->t_flags&TF_KEEPSTACK)
  435.             ? &dummy-16 : t_stack->top());
  436. #endif    (i386 || sun)
  437.  
  438. #if (ns32000 || vax)
  439.         t_fp = FP(dummy);
  440.         if ((this->t_flags&TF_KEEPSTACK) == 0)    {
  441.             //
  442.             // remember current sp
  443.             //
  444. #ifdef ns32000
  445.             { asm("sprd    sp, __rtmp"); }
  446. #endif
  447. #ifdef vax
  448.             { asm("movl    sp, -8(fp)"); }
  449. #endif
  450.             t_csp = (int*)_rtmp;            
  451.  
  452.             //
  453.             // load new sp with the top of the new stack
  454.             //
  455.             _rtmp = (int)(t_stack->top());
  456.             // lose any stored regs from previous context...
  457.             // but not important.
  458. #ifdef vax
  459.             { asm("movl    -8(fp), sp"); }
  460. #endif
  461. #ifdef ns32000
  462.             { asm("lprd    sp, __rtmp"); }
  463. #endif
  464.         }
  465. #endif    (vax || ns32000)
  466.  
  467.         // We invoke the saved callstate with a "zero" sp
  468.         // indicator to say "just go ahead and use whatever
  469.         // stack you are currently running on.
  470.         //
  471.  
  472.         (void)this->t_callstate.call(0,t_boundobj);
  473.  
  474.         // NOT REACHED:
  475.         // We can never return normally from the invocation
  476.         // since the fp which is created on the call points
  477.         // back to the stack of the processor thread which
  478.         // is handling the invocation.  Since its not likely
  479.         // that processor's thread will be in the same state 
  480.         // when the invocation return we can't use it for
  481.         // anything.  callstate->call must swtch back to us, after
  482.         // returning the thread to the free pool.
  483.         //
  484.         // We wouldn't have to worry about any of this if we
  485.         // were running on a uniprocessor
  486.         //
  487.         error("Return to runrun!");
  488.         
  489.     }  else    {
  490.         (void)swtch();    
  491.     //
  492.     // NOTREACHED
  493.     //        
  494.     }
  495.     return 0;        // for the compiler only
  496. }
  497.  
  498.  
  499. //
  500. // Go to sleep on a synchronization object.  
  501. // The synchronization object has already taken care of the semantics
  502. // of going to sleep.  We just change our state and conk out...
  503. //
  504. void
  505. Thread::sleep(SynchroObject* so = 0)
  506. {
  507.  
  508.     if (this != thisthread)
  509.         error("this!=thisthread in sleep. HELP!");
  510.         
  511.     t_blockedon = so;
  512.     
  513.     if ((t_state&TS_RUNNING)==0)
  514.         error("Can't block a non running thread");
  515.  
  516.     (void)swtch();
  517.     // wakeup here    
  518. }
  519.  
  520.  
  521. double 
  522. Thread::cputime()
  523. {
  524.     return 0;     
  525. }
  526.  
  527. int
  528. Thread::canpreempt()
  529.     if ((t_state&TS_RUNNING) &&            // is running
  530.        ((t_flags&TF_NONPREEMPTABLE) == 0)    &&     // is preemptable
  531.        ( t_slockcount == 0) &&            // not in spinlock cs
  532.        ((t_state&TS_VIRGIN) == 0)    &&        // is not a virgin
  533.          t_expired)                    // has run long enough
  534.     {
  535.         t_expired = 1;
  536.         return 1;
  537.     } else {
  538.         t_expired = 1;
  539.         return 0;
  540.     }
  541. }
  542.  
  543. //
  544. // Threads come here when they want to terminate.  If they send
  545. // any arguments, it will be returned to any thread which waits
  546. // to join on thisthread (assuming someone has marked it as TF_WILLJOIN)
  547. //
  548. // All joining (and joinee) threads serialize at a single j_lock.
  549. // Until this is demonstrated to be a problem, it will stand.
  550. //
  551. // Note: this code is called in the context of the terminating
  552. // thread.  The thread destructor is automatically invoked in the
  553. // context of the scheduler thread when we return to it via swtch().
  554. //
  555.  
  556. static shared_t Spinlock j_lock;    // share single lock
  557.  
  558. void
  559. Thread::terminate(Objany retobj=0)
  560. {
  561.     int rtmp;
  562.  
  563.     if (this != thisthread)    {
  564.         thisthread->error("terminate: Can only finish self");
  565.         return;
  566.     }
  567.         
  568.     if (t_flags&TF_WILLJOIN)    {
  569.         Thread *t;
  570.         j_lock.lock();            
  571.         
  572.         orstate(TS_FINISHED);
  573.         t = t_jthread;            // shared union
  574.         t_jvalue = retobj;
  575.         if (t)        {    // someone waiting
  576.             j_lock.unlock();
  577.             t->wakeup((SynchroObject*)this);
  578.         } else
  579.             j_lock.unlock();
  580.     } else
  581.         orstate(TS_FINISHED);
  582.  
  583. #ifdef debug
  584.     dout << form("terminate: thread %x t_csp %x t_fp %x\n",
  585.         this, t_csp, t_fp);
  586. #endif debug
  587.  
  588.     this->swtch();
  589.     // NOT REACHED
  590.     return;
  591. }
  592.  
  593. //
  594. // Join:
  595. //
  596. //    If a thread is joining on another thread, block that thread
  597. //    until the joined thread terminates.  When it does, the joiner
  598. //    is responsible for deleting it.
  599. //
  600.  
  601.  
  602. void
  603. Thread::willjoin()
  604. {
  605.     if (t_flags&TF_WILLJOIN)
  606.         thisthread->error("Can only join once on a thread");
  607.     else
  608.         t_flags |= TF_WILLJOIN;
  609. }    
  610.  
  611. Objany
  612. Thread::join()
  613. {
  614.     Thread *me = thisthread;
  615.     
  616. #ifdef debug
  617.     dout << form("join: thread %x joining on %x\n", thisthread, this);
  618. #endif debug
  619.  
  620.     // want to join on someone else.  Wait until they finish
  621.     //
  622.     
  623.     if ( (t_flags&TF_WILLJOIN) == 0)
  624.         error("Can't join on a free thread");
  625.     
  626.     j_lock.lock();
  627.     if (this->state()&TS_FINISHED)    {    // thread is done,
  628.         j_lock.unlock();        // return results
  629.     } else    {                // sleep on "this"
  630.         me->orstate(TS_BLOCKED);
  631.         t_jthread = me;            // careful of coercion
  632.         me->nonpreemptable();
  633.         j_lock.unlock();
  634. #ifdef debug
  635.         dout << "join: thread not finished, sleeping\n";
  636. #endif debug
  637.         me->sleep((SynchroObject*)this);
  638.         me->preemptable();
  639.     }
  640.     
  641.     if ((this->state()&TS_FINISHED) == 0)
  642.         this->error("erroneous join");
  643.     
  644.     Objany retobj = t_jvalue;
  645.     delete this;            // will wait for other to terminate
  646.     return retobj;
  647. }
  648.  
  649. /*
  650. void
  651. Thread::setaffinity(Process* p)
  652. {
  653.     t_proc = p;    // shouldn't really use t_proc here
  654. }
  655. */
  656.  
  657. void
  658. Thread::print(ostream& s)
  659. {
  660.     s << "Thread:";
  661.     Object::print(s);     // get base class to show 
  662.     s << "\n"; 
  663.     s << form("t_sp=0x%x, t_sp.limit=0x%x t_ssz=0x%x, t_csp=0x%x, t_fp=0x%x\n",
  664.            stack()->top(), stack()->limit(),stack()->size(), t_csp, t_fp) <<
  665.       form("t_state=0x%x, t_flags=0x%x, t_tag=%d, t_tid=%d, t_pri=%d\n",
  666.             t_state,    t_flags,    t_tag,  t_tid, t_pri);
  667. //    s << "t_timer=" << &t_timer << "\n";
  668.     s << "t_proc=" << t_proc << "\n";
  669.     s << "t_callstate=" << t_callstate << "\n";
  670.     s << "t_blockedon=" << t_blockedon << "\n";
  671. }
  672.  
  673.  
  674.  
  675. ThreadQUnlocked::ThreadQUnlocked(int neededstate, Thread *t = 0)    :(t)
  676. {
  677.     tq_neededstate = neededstate;
  678. }
  679.  
  680.  
  681. ThreadQUnlocked::~ThreadQUnlocked()
  682. {
  683.     Thread *t;
  684.     while (t = get())    {
  685.         t->setstate(TS_DELETE);
  686.         delete (Thread*)t;
  687.     }
  688. }
  689.  
  690.  
  691.  
  692. ThreadQ::ThreadQ(int neededstate, Thread *t = 0)    : (t)
  693. {
  694.     tq_lock = new Spinlock;
  695.     tq_neededstate = neededstate;
  696.     tq_length = 0;
  697. }
  698.  
  699.  
  700. //
  701. // Delete a threadQ and all of the threads sitting on it.
  702. // We do not bother to return the threads to the reclaimq.
  703. // (in fact, we don't even bother to invoke the threads potentially
  704. // virtual constructor becuase the derived thread class may have
  705. // already seen this thread destroyed.  Memory management needs to be
  706. // separated from object cleanup.
  707. //
  708. ThreadQ::~ThreadQ()
  709. {
  710.     Thread *t;
  711.     while (t = get())    {
  712.         t->setstate(TS_DELETE);
  713.         delete (Thread*)t;        
  714.     }
  715. }
  716.  
  717. int
  718. Thread::tagcnt()
  719. {
  720.     return int(t_freetag);
  721. }
  722.  
  723.         
  724. void
  725. ThreadQ::print(ostream& s)
  726. {
  727.     s << form("(ThreadQ)this=0x%x, tq_neededstate=0x%x, tq_length=0x%x",
  728.         this, tq_neededstate, tq_length);
  729.     s << " tq_lock= " << tq_lock << "\n";
  730.     s << "\tElements: ";
  731.     Oqueue::print(s);
  732. }
  733.  
  734. void 
  735. ThreadQUnlocked::print(ostream& s)
  736. {
  737.     s << form( "(ThreadQUnlocked)this=0x%x, tq_neededstate=0x%x",  
  738.         this, tq_neededstate);
  739.     s << "\tElements: ";
  740.     Oqueue::print(s);
  741. }
  742.